/*
 * Copyright (C) 2012 Damien Girard <dgirard@nativesoft.fr>
 * Copyright (C) 2012 Sebastien Baudrand <sebbaudrand@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package fr.umlv.qroxy.communication;

import fr.umlv.qroxy.cache.CacheServer;
import fr.umlv.qroxy.diagnostic.WebServer;
import fr.umlv.qroxy.rules.HeaderHttp;
import fr.umlv.qroxy.rules.RuleChecker;
import gnu.getopt.Getopt;
import gnu.getopt.LongOpt;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Main class of Qroxy.
 *
 * @author Damien Girard <dgirard@nativesoft.fr>
 * @author Sebastien Baudrand <sebbaudrand@gmail.com>
 */
public class Qroxy {

    private static String CONF_PATH = System.getProperty("user.home") + File.separator + "qroxy" + File.separator + "conf" + File.separator + "conf.qroxy";
    private static String CACHE_PATH = System.getProperty("user.home") + File.separator + "qroxy" + File.separator + "qroxyCache";
    public static final RuleChecker RULE_CHECKER = new RuleChecker();
    private static int PROXY_PORT = 8080;
    private static int NETWORK_CACHE_PORT = 5000;
    private static int DIAGNOSTIC_CACHE_PORT = 4242;
    private static Level DEBUG_LEVEL = Level.INFO;

    public static void main(String[] argv) throws IOException, Exception {
        // Handling command parameters. (Using GetOpt for Java)
        int c;
        String arg;
        LongOpt[] longopts = new LongOpt[7];
        // 
        StringBuffer sb = new StringBuffer();
        longopts[0] = new LongOpt("help", LongOpt.NO_ARGUMENT, null, 'h');
        longopts[1] = new LongOpt("cache-dir", LongOpt.REQUIRED_ARGUMENT, sb, 'C');
        longopts[2] = new LongOpt("config", LongOpt.REQUIRED_ARGUMENT, sb, 'c');
        longopts[3] = new LongOpt("debug-level", LongOpt.REQUIRED_ARGUMENT, sb, 'd');
        longopts[4] = new LongOpt("port", LongOpt.REQUIRED_ARGUMENT, sb, 'p');
        longopts[5] = new LongOpt("cacheport", LongOpt.REQUIRED_ARGUMENT, sb, 'P');
        longopts[6] = new LongOpt("diagnosticport", LongOpt.REQUIRED_ARGUMENT, sb, 'D');

        // 
        Getopt g = new Getopt("Qroxy", argv, "h:C:c:d:p:P:D:", longopts);
        g.setOpterr(false); // We'll do our own error handling
        //
        File f;
        Integer port;
        while ((c = g.getopt()) != -1) {
            switch (c) {
                case 'C':
                    arg = g.getOptarg();
                    f = new File(arg);
                    if (!f.exists() || !f.isDirectory()) {
                        System.err.println("Cache directory does not exists. " + f.getPath());
                        return;
                    }
                    CACHE_PATH = arg;
                    break;
                case 'c':
                    arg = g.getOptarg();
                    f = new File(arg);
                    if (!f.exists() || !f.canRead()) {
                        System.err.println("Cannot read the cache configuration file. " + f.getPath());
                        return;
                    }
                    CONF_PATH = arg;
                    break;
                case 'p':
                case 'P':
                case 'D':
                    arg = g.getOptarg();
                    try {
                        port = Integer.parseInt(arg);
                    } catch (Exception e) {
                        System.err.println("Cannot parse the port number: " + arg + ". " + e);
                        return;
                    }
                    switch (c) {
                        case 'p':
                            PROXY_PORT = port;
                            break;
                        case 'P':
                            NETWORK_CACHE_PORT = port;
                            break;
                        case 'D':
                            DIAGNOSTIC_CACHE_PORT = port;
                            break;
                    }
                    break;
                case 'd':
                    arg = g.getOptarg();
                    try {
                        DEBUG_LEVEL = Level.parse(arg);
                    } catch (Exception e) {
                        System.err.println("Invalid debug level value: " + arg + ". " + e);
                        return;
                    }
                    break;
                case 'h':
                    // Displaying help.
                    System.out.println("Qroxy - (c) Damien Girard and Sebastien Baudrand");
                    System.out.println("Usage: Qroxy");
                    System.out.println("Option:");

                    System.out.println("\t-c <file>, --config <file>\n\t    Configuration file");
                    System.out.println("\t-C <dir>, --cache-dir <dir>\n\t    Directory that hold the cache");
                    System.out.println("\t-d <level>, --debug-level <level>\n\t    Debug level. (Possible value: ALL, FINE, FINER, FINEST");
                    System.out.println("\t-p <port>, --port <port>\n\t    Proxy port");
                    System.out.println("\t-P <port>, --cacheport <port>\n\t    Proxy network cache port");
                    System.out.println("\t-D <port>, --diagnosticport <port>\n\t    Diagnostic port");
                    System.out.println("\t-h, --help\n\t    Shows this help");

                    return;
                case ':':
                    System.out.println("Doh! You need an argument for option "
                            + (char) g.getOptopt());
                    break;
                //
                case '?':
                    System.out.println("The option '" + (char) g.getOptopt()
                            + "' is not valid");
                    break;
                //
                default:
                    System.out.println("getopt() returned " + c);
                    break;
            }
        }
        // Launching Proxy.
        System.out.println("-> Starting Qroxy with following parameters: ");
        System.out.println("Configuration file: " + CONF_PATH);
        System.out.println("Cache directory: " + CACHE_PATH);
        System.out.println("Port: " + PROXY_PORT);
        System.out.println("Network cache port: " + NETWORK_CACHE_PORT);
        System.out.println("Diagnostic cache port: " + DIAGNOSTIC_CACHE_PORT);
        System.out.println("Debug level: " + DEBUG_LEVEL.getName());

        // Parameters parsed. Doing somes checks.
        if (!new File(CONF_PATH).exists()) {
            System.err.println("FATAL ERROR: Configuration file not found: "+ CONF_PATH);
            return;
        }
        if (!new File(CACHE_PATH).exists()) {
            System.err.println("FATAL ERROR: Cache directory not found: "+ CACHE_PATH);
            return;
        }
        
        // Setting up the logs.
        Handler consoleHandler = new ConsoleHandler();
        consoleHandler.setLevel(DEBUG_LEVEL);

        Logger.getLogger(Server.class.getName()).setLevel(DEBUG_LEVEL);
        Logger.getLogger(Server.class.getName()).setUseParentHandlers(false);
        Logger.getLogger(Server.class.getName()).addHandler(consoleHandler);
        Logger.getLogger(Connection.class.getName()).setLevel(DEBUG_LEVEL);
        Logger.getLogger(Connection.class.getName()).setUseParentHandlers(false);
        Logger.getLogger(Connection.class.getName()).addHandler(consoleHandler);
        Logger.getLogger(HeaderHttp.class.getName()).setLevel(DEBUG_LEVEL);
        Logger.getLogger(HeaderHttp.class.getName()).setUseParentHandlers(false);
        Logger.getLogger(HeaderHttp.class.getName()).addHandler(consoleHandler);

        Logger.getLogger("Cache").setLevel(DEBUG_LEVEL);
        Logger.getLogger("Cache").setUseParentHandlers(false);
        Logger.getLogger("Cache").addHandler(consoleHandler);

        Logger.getLogger(WebServer.class.getName()).setLevel(DEBUG_LEVEL);
        Logger.getLogger(WebServer.class.getName()).setUseParentHandlers(false);
        Logger.getLogger(WebServer.class.getName()).addHandler(consoleHandler);

        // Reading the rules.

        RULE_CHECKER.addRuleFile(new File(CONF_PATH));

        // Launching cache server.
        CacheServer.serve(20, CACHE_PATH, NETWORK_CACHE_PORT);

        //Launching web interface.
        WebServer web = new WebServer(DIAGNOSTIC_CACHE_PORT, CONF_PATH, new File(CACHE_PATH), RULE_CHECKER.getRules());
        Thread t = new Thread(web);
        t.start();

        // Launching proxy server.
        Server server = new Server(PROXY_PORT, CACHE_PATH);
        server.run();
    }
}
